<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="本文主要是网络基础——TCP协议的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV06-02-网络基础-07-TCP协议">
<meta property="og:url" content="https://sumumm.github.io/post/8a2d2090.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="本文主要是网络基础——TCP协议的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623113754219.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623120707723.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623141216063.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623142905506.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623142923675.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143444725.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143622193.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143730304.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623150318856.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/tcpip007.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133727320.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133759867.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133840905.png">
<meta property="article:published_time" content="2024-10-27T03:49:28.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:56.998Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="LV06-网络编程">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623113754219.png">


<link rel="canonical" href="https://sumumm.github.io/post/8a2d2090.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":"","permalink":"https://sumumm.github.io/post/8a2d2090.html","path":"post/8a2d2090.html","title":"LV06-02-网络基础-07-TCP协议"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV06-02-网络基础-07-TCP协议 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81TCP%E5%8D%8F%E8%AE%AE%E7%AE%80%E4%BB%8B"><span class="nav-text">一、TCP协议简介</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E4%BB%80%E4%B9%88%E6%98%AFTCP"><span class="nav-text">1. 什么是TCP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9ATCP%E8%BF%9E%E6%8E%A5"><span class="nav-text">2. 如何确定TCP连接</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-TCP%E6%9C%80%E5%A4%A7%E8%BF%9E%E6%8E%A5%E6%95%B0%EF%BC%9F"><span class="nav-text">3. TCP最大连接数？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-TCP%E5%8D%8F%E8%AE%AE%E7%89%B9%E7%82%B9"><span class="nav-text">4. TCP协议特点</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-%E5%8F%AF%E9%9D%A0%E6%80%A7%E4%BC%A0%E8%BE%93%E5%8E%9F%E7%90%86"><span class="nav-text">5. 可靠性传输原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%AF%E9%9D%A0%E6%80%A7%E4%BC%A0%E8%BE%93"><span class="nav-text">5.1 什么是可靠性传输</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-%E5%8F%AF%E9%9D%A0%E6%80%A7%E4%BC%A0%E8%BE%93%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86"><span class="nav-text">5.2 可靠性传输的基本原理</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="nav-text">6. 应用场景</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81TCP%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F"><span class="nav-text">二、TCP报文格式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E6%8A%A5%E6%96%87%E5%9B%BE%E7%A4%BA"><span class="nav-text">1. 报文图示</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%90%84%E5%AD%97%E6%AE%B5%E8%AF%B4%E6%98%8E"><span class="nav-text">2. 各字段说明</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-%E6%BA%90%E7%AB%AF%E5%8F%A3%E5%92%8C%E7%9B%AE%E7%9A%84%E7%AB%AF%E5%8F%A3%E5%AD%97%E6%AE%B5"><span class="nav-text">2.1 源端口和目的端口字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-%E5%BA%8F%E5%88%97%E5%8F%B7%E5%AD%97%E6%AE%B5"><span class="nav-text">2.2 序列号字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-%E7%A1%AE%E8%AE%A4%E5%BA%94%E7%AD%94%E5%AD%97%E6%AE%B5"><span class="nav-text">2.3 确认应答字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-%E6%95%B0%E6%8D%AE%E5%81%8F%E7%A7%BB%E5%AD%97%E6%AE%B5"><span class="nav-text">2.4 数据偏移字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-5-%E4%BF%9D%E7%95%99%E5%AD%97%E6%AE%B5"><span class="nav-text">2.5 保留字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-6-%E6%A0%87%E5%BF%97%E4%BD%8D%E5%AD%97%E6%AE%B5"><span class="nav-text">2.6 标志位字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-7-%E7%AA%97%E5%8F%A3%E5%A4%A7%E5%B0%8F%E5%AD%97%E6%AE%B5"><span class="nav-text">2.7 窗口大小字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-8-TCP%E6%A0%A1%E9%AA%8C%E5%92%8C%E5%AD%97%E6%AE%B5"><span class="nav-text">2.8 TCP校验和字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-9-%E7%B4%A7%E6%80%A5%E6%8C%87%E9%92%88%E5%AD%97%E6%AE%B5"><span class="nav-text">2.9 紧急指针字段</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81TCP%E5%BB%BA%E7%AB%8B%E8%BF%9E%E6%8E%A5"><span class="nav-text">三、TCP建立连接</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E8%BF%87%E7%A8%8B"><span class="nav-text">1. 三次握手过程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%EF%BC%9F"><span class="nav-text">2. 为什么是三次握手？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E4%B8%A4%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E5%BA%8F%E5%88%97%E5%8F%B7"><span class="nav-text">3. 两个重要的序列号</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E5%88%9D%E5%A7%8B%E5%BA%8F%E5%88%97%E5%8F%B7"><span class="nav-text">3.1 初始序列号</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-%E5%BA%94%E7%AD%94%E5%BA%8F%E5%88%97%E5%8F%B7"><span class="nav-text">3.2 应答序列号</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E6%8F%A1%E6%89%8B%E5%8F%AF%E4%BB%A5%E6%90%BA%E5%B8%A6%E6%95%B0%E6%8D%AE%E5%90%97%EF%BC%9F"><span class="nav-text">4. 握手可以携带数据吗？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-TCP%E6%8F%A1%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">5. TCP握手丢失？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%8F%A1%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">5.1 第一次握手丢失？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-%E7%AC%AC%E4%BA%8C%E6%AC%A1%E6%8F%A1%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">5.2 第二次握手丢失？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-%E7%AC%AC%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">5.3 第三次握手丢失？</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%9B%9B%E3%80%81TCP%E8%BF%9E%E6%8E%A5%E6%96%AD%E5%BC%80"><span class="nav-text">四、TCP连接断开</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E8%BF%87%E7%A8%8B"><span class="nav-text">1. 四次挥手过程</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AF%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%EF%BC%9F"><span class="nav-text">2. 为什么是四次挥手？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E6%8C%A5%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">3. 挥手丢失？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E7%AC%AC%E4%B8%80%E6%AC%A1%E6%8C%A5%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">3.1 第一次挥手丢失？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-%E7%AC%AC%E4%BA%8C%E6%AC%A1%E6%8C%A5%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">3.2 第二次挥手丢失？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-%E7%AC%AC%E4%B8%89%E6%AC%A1%E6%8C%A5%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">3.3 第三次挥手丢失？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-4-%E7%AC%AC%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B%E4%B8%A2%E5%A4%B1%EF%BC%9F"><span class="nav-text">3.4 第四次挥手丢失？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-2MSL%E5%85%B7%E4%BD%93%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">4. 2MSL具体是什么？</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%94%E3%80%81TCP%E7%8A%B6%E6%80%81%E8%AF%B4%E6%98%8E"><span class="nav-text">五、TCP状态说明</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-TCP%E7%9A%84%E7%8A%B6%E6%80%81"><span class="nav-text">1. TCP的状态</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E7%BB%93%E5%90%88%E5%9B%BE%E6%96%87%E5%88%86%E6%9E%90%E4%B8%80%E4%B8%8B"><span class="nav-text">2. 结合图文分析一下</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%85%AD%E3%80%81%E6%80%BB%E7%BB%93"><span class="nav-text">六、总结</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/8a2d2090.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV06-02-网络基础-07-TCP协议 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV06-02-网络基础-07-TCP协议
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2024-10-27 11:49:28" itemprop="dateCreated datePublished" datetime="2024-10-27T11:49:28+08:00">2024-10-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/" itemprop="url" rel="index"><span itemprop="name">01HQ课程体系</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/" itemprop="url" rel="index"><span itemprop="name">LV06-网络编程</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>17k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>1:02</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>本文主要是网络基础——TCP协议的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" width=150px>Windows</td>        <td align="left">windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu16.04的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 16 Pro</td>        <td align="left">16.2.3 build-19376536</td>      </tr>    <tr>        <td align="center">SecureCRT</td>        <td align="left">Version 8.7.2 (x64 build 2214)   -   正式版-2020年5月14日</td>      </tr>    <tr>        <td align="center">开发板</td>        <td align="left">正点原子 i.MX6ULL Linux阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.15(NXP官方提供)</td>      </tr>    <tr>        <td align="center">STM32开发板</td>        <td align="left">正点原子战舰V3(STM32F103ZET6)</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr><td align="center">参考方向  </td><td align="center">参考原文</td></tr>    <tr><td align="center" width=150px>TCP协议</td><td align="left"><a href="https://xiaolincoding.com/network/" target="_blank">图解网络介绍 | 小林coding (xiaolincoding.com)  <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="center" width=150px>TCP 可靠性传输原理</td><td align="left"><a href="https://blog.csdn.net/summer85/article/details/84369145" target="_blank">Tcp可靠性传输的原理分析_summer85的博客-CSDN博客  <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="center" width=150px>TCP 报文格式</td><td align="left"><a href="http://c.biancheng.net/view/6441.html" target="_blank">TCP报文格式解析 (biancheng.net) <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="center" width=150px>TCP 三次握手</td><td align="left"><a href="https://blog.csdn.net/guoweimelon/article/details/50878730" target="_blank">TCP三次握手建立连接 <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="center" width=150px>TCP 四次握手</td><td align="left"><a href="https://blog.csdn.net/guoweimelon/article/details/50879302" target="_blank">TCP四次握手释放连接 <i class="fa fa-external-link-alt"></i> </a></td></tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">---</td>        <td align="left">--- <a href="" target="_blank">  <i class="fa fa-external-link-alt"></i></a></td>      </tr></table>
              </div>
            </details>

<h1 id="一、TCP协议简介"><a href="#一、TCP协议简介" class="headerlink" title="一、TCP协议简介"></a><font size=3>一、<code>TCP</code>协议简介</font></h1><h2 id="1-什么是TCP"><a href="#1-什么是TCP" class="headerlink" title="1. 什么是TCP"></a><font size=3>1. 什么是<code>TCP</code></font></h2><p>传输控制协议（<code>Transmission Control Protocol</code>，<code>TCP</code>）是一种<strong>面向连接</strong>的、<strong>可靠</strong>的、<strong>基于字节流</strong>的<strong>传输层通信协议</strong>。在 <code>TCP</code> 协议中，通过三次握手建立连接。通信结束后，还需要断开连接。如果在发送数据包时，没有正确被发送到目的地时，将会重新发送数据包。</p>
<p>为什么需要<code>TCP</code>协议呢？<code>IP</code> 层（也就是网络层）是不可靠的，它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性，那么就需要由上层（传输层）的 <code>TCP</code> 协议来负责。因为 <code>TCP</code> 是一个工作在<strong>传输层</strong>的<strong>可靠</strong>数据传输的服务，它能确保接收端接收的网络包是<strong>无损坏、无间隔、非冗余和按序的。</strong></p>
<p><code>TCP</code> 协议的数据包进行传输采用的是服务器端和客户端模式。发送<code>TCP</code>数据请求方为客户端，另一方则为服务器端。客户端要与服务器端进行通信，服务器端必须开启监听的端口，客户端才能通过端口连接到服务器，然后进行通信。</p>
<h2 id="2-如何确定TCP连接"><a href="#2-如何确定TCP连接" class="headerlink" title="2. 如何确定TCP连接"></a><font size=3>2. 如何确定<code>TCP</code>连接</font></h2><p>用于保证可靠性和流量控制维护的某些状态信息，这些信息的组合，包括<code>Socket</code>、序列号和窗口大小称为<strong>连接</strong>。其中<code>socket</code>，由 <code>IP</code> 地址和端口号组成；序列号，用来解决乱序问题等；窗口大小，用来做流量控制。</p>
<p>那我们如何确定一个<code>TCP</code>连接呢？这就需要<strong>源地址</strong>、<strong>源端口</strong>、<strong>目的地址</strong>和<strong>目的端口</strong>四个参数，这便可以唯一确定一个<code>TCP</code>连接。源地址和目的地址的字段（<code>32</code>位）是在 <code>IP</code> 头部中，作用是通过 <code>IP</code> 协议发送报文给对方主机。源端口和目的端口的字段（<code>16</code>位）是在 <code>TCP</code> 头部中，作用是告诉 <code>TCP</code> 协议应该把报文发给哪个进程。</p>
<h2 id="3-TCP最大连接数？"><a href="#3-TCP最大连接数？" class="headerlink" title="3. TCP最大连接数？"></a><font size=3>3. <code>TCP</code>最大连接数？</font></h2><p>服务器通常固定在某个本地端口上监听，等待客户端的连接请求。因此，客户端 <code>IP</code> 和 端口是可变的，其理论值计算公式如下</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">最大TCP连接数 = 客户端IP数 X 客户端的端口数</span><br></pre></td></tr></table></figure>

<p>对 <code>IPv4</code>，客户端的 <code>IP</code> 数最多为 <code>2</code> 的 <code>32</code> 次方，客户端的端口数最多为 <code>2</code> 的 <code>16</code> 次方，也就是服务端单机最大 <code>TCP</code> 连接数，约为 <code>2</code> 的 <code>48</code> 次方。但是吧这只是理论上，实际服务端最大并发 <code>TCP</code> 连接数远不能达到理论上限，会受以下因素影响：</p>
<ul>
<li><p><strong>文件描述符限制</strong></p>
<p>每个 <code>TCP</code> 连接都是一个文件，如果文件描述符被占满了，会发生 <code>too many open files</code>。<code>Linux</code> 对可打开的文件描述符的数量分别作了三个方面的限制：</p>
<table>
    <tr><td align="center" width=150px>系统级</td><td align="left">当前系统可打开的最大数量，通过 cat /proc/sys/fs/file-max 查看</td></tr>
    <tr><td align="center" width=150px>用户级</td><td align="left">指定用户可打开的最大数量，通过 cat /etc/security/limits.conf 查看</td></tr>
    <tr><td align="center" width=150px>进程级</td><td align="left">单个进程可打开的最大数量，通过 cat /proc/sys/fs/nr_open 查看</td></tr>
</table>
</li>
<li><p><strong>内存限制</strong></p>
</li>
</ul>
<p>每个 <code>TCP</code> 连接都要占用一定内存，操作系统的内存是有限的，如果内存资源被占满后，会发生 <code>OOM</code>。啥是<code>OOM</code>？其实前边内存管理的笔记有提到过， <code>Linux</code>内核有个机制叫<code>OOM killer</code>（<code>Out-Of-Memory killer</code>），该机制会监控那些占用内存过大，尤其是瞬间很快消耗大量内存的进程，为了防止内存耗尽内核会把该进程杀掉。</p>
<h2 id="4-TCP协议特点"><a href="#4-TCP协议特点" class="headerlink" title="4. TCP协议特点"></a><font size=3>4. <code>TCP</code>协议特点</font></h2><p><code>TCP</code> 协议使用的是面向连接的方法进行通信的，特点如下：</p>
<ul>
<li>（1）TCP 是一个面向连接的协议</li>
</ul>
<p>无论哪一方向另一方发送数据之前，都必须先在双方之间建立一个 TCP连接，否则将无法发送数据，通过三次握手建立连接。  </p>
<ul>
<li>（2）面向流的处理</li>
</ul>
<p><code>TCP</code> 以流的方式处理数据。也就是说，<code>TCP</code> 可以一个字节一个字节地接收数据，而不是一次接收一个预定格式的数据块。<code>TCP</code> 把接收到的数据组成长度不等的段，将数据按字节大小进行编号，再传递到网络层。接收端通过 ACK 来确认收到的数据编号，通过这种机制能够保证 TCP 协议的有序性和完整性，因此 TCP 能够提供可靠性传输。  </p>
<ul>
<li>（3）确认与重传</li>
</ul>
<p>当数据从主机 A 发送到主机 B 时，主机 B 会返回给主机 A 一个确认应答； TCP 通过确认应答 ACK 实现可靠的数据传输。当发送端将数据发送出去之后会等待对端的确认应答。如果有确认应答，说明数据已经成功到达对端。反之，数据丢失的可能性比较大。</p>
<p>在一定的时间内如果没有收到确认应答，发送端就可以认为数据已经丢失，并进行重发。由此，即使产生了丢失，仍然可以保证数据能够到达对端，实现可靠传输。  </p>
<ul>
<li><p>（4）提供全双工通信，<code>TCP</code> 允许通信双方的应用进程在任何时候都能发送数据。<code>TCP</code> 连接的两端都设有发送缓存和接受缓存，用</p>
<p>来临时存放双向通信的数据。</p>
</li>
<li><p>（5）是端到端的通信，每一条<code>TCP</code>连接只能有两个端点，每一条 TCP 连接只能是点对点的(一对一)。</p>
</li>
<li><p>（6）重新排序</p>
</li>
</ul>
<p>TCP 协议除了确认应答与重传机制外， TCP 协议也会采用校验和的方式来检验数据的有效性，主机在接收数据的时候，会将重复的报文丢弃，将乱序的报文重组，发现某段报文丢失了会请求发送方进行重发，因此在 TCP 往上层协议递交的数据是顺序的、无差错的完整数据。  </p>
<details class="folding-tag" blue><summary> 点击查看 TCP 如何保证数据的顺序化传输 </summary>
              <div class='content'>
              <p>（1）主机每次发送数据时，<code>TCP</code>就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认;</p><p>（2）如果发送主机在一个特定时间内没有收到接收主机的确认，则发送主机会重传此数据包；</p><p>（3）接收主机利用序列号对接收的数据进行确认，以便检测对方发送的数据是否有丢失或者乱序等；</p><p>（4）接收主机一旦收到已经顺序化的数据，它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。</p>
              </div>
            </details>

<ul>
<li>（7）拥塞控制</li>
</ul>
<p>如果网络上的负载（发送到网络上的分组数）大于网络上的容量（网络同时能处理的分组数），就可能引起拥塞，判断网络拥塞的两个因素：延时和吞吐量。拥塞控制机制是：开环（预防）和闭环（消除）。</p>
<p>流量控制是通过接收方来控制流量的一种方式；而拥塞控制则是通过发送方来控制流量的一种方式。TCP 发送方可能因为 IP 网络的拥塞而被遏制， TCP 拥塞控制就是为了解决这个问题（注意和 TCP 流量控制的区别）。</p>
<p>TCP 拥塞控制的几种方法：慢启动，拥塞避免，快重传和快恢复。  </p>
<ul>
<li>（8）流量控制（滑动窗口协议）</li>
</ul>
<p>TCP 流量控制主要是针对接收端的处理速度不如发送端发送速度快的问题，消除发送方使接收方缓存溢出的可能性。 TCP 流量控制主要使用滑动窗口协议，滑动窗口是接受数据端使用的窗口大小，用来告诉发送端接收端的缓存大小，以此可以控制发送端发送数据的大小，从而达到流量控制的目的。这个窗口大小就是我们一次传输几个数据。对所有数据帧按顺序赋予编号，发送方在发送过程中始终保持着一个发送窗口，只有落在发送窗口内的帧才允许被发送；同时接收方也维持着一个接收窗口，只有落在接收窗口内的帧才允许接收。这样通过调整发送方窗口和接收方窗口的大小可以实现流量控制。  </p>
<h2 id="5-可靠性传输原理"><a href="#5-可靠性传输原理" class="headerlink" title="5. 可靠性传输原理"></a><font size=3>5. 可靠性传输原理</font></h2><h3 id="5-1-什么是可靠性传输"><a href="#5-1-什么是可靠性传输" class="headerlink" title="5.1 什么是可靠性传输"></a><font size=3>5.1 什么是可靠性传输</font></h3><p>在目前的网络栈协议族中，在需要提供可靠性数据传输的应用中，<code>TCP</code> 协议是首选的，有时也是唯一的选择。<code>TCP</code>协议工作在传输层，它主要完成的工作有：</p>
<p>（1）提供多路复用。</p>
<p>（2）实现数据基本传输功能。</p>
<p>（3）建立通信通道。</p>
<p>（4）提供流量控制。</p>
<p>（5）提供数据可靠性传输保证。</p>
<p>其中数据可靠性传输保证是其中最为重要的方面，也是<code>TCP</code>协议区别于其它协议的最重要特性。所谓提供数据可靠性传输不仅仅指将数据成功的由本地主机传送到远端主机，数据可靠性传输包括如下内容：</p>
<p>（1）能够处理数据传输过程中被破坏问题。</p>
<p>（2）能够处理重复数据接收问题。</p>
<p>（3）能够发现数据丢失以及对此进行有效解决。</p>
<p>（4）能够处理接收端数据乱序到达问题。</p>
<h3 id="5-2-可靠性传输的基本原理"><a href="#5-2-可靠性传输的基本原理" class="headerlink" title="5.2 可靠性传输的基本原理"></a><font size=3>5.2 可靠性传输的基本原理</font></h3><p>那<code>TCP</code>可靠性传输实现的基本原理是什么呢？<code>TCP</code> 协议必须提供对所有这些问题的解决方案方可保证其所声称的数据可靠性传输。</p>
<p><code>TCP</code>协议规范和当前绝大多数<code>TCP</code> 协议实现代码均采用数据重传和数据确认应答机制来完成<code>TCP</code> 协议的可靠性数据传输。数据超时重传和数据应答机制的基本前提是对每个传输的字节进行编号，即我们通常所说的序列号。数据超时重传是发送端在某个数据包发送出去，在一段固定时间后如果没有收到对该数据包的确认应答，则（假定该数据包在传输过程中丢失）重新发送该数据包。而数据确认应答是指接收端在成功接收到一个有效数据包后，发送一个确认应答数据包给发送端主机，该确认应答数据包中所包含的应答序列号即指已接收到的数据中最后一个字节的序列号加<code>1</code>，加<code>1 </code>的目的在于指出此时接收端期望接收的下一个数据包中第一个字节的序列号。<strong>数据超时重传</strong>和<strong>数据确认应答</strong>以及<strong>对每个传输的字节分配序列号</strong>是<code>TCP</code> 协议提供可靠性数据传输的<strong>核心本质</strong>。</p>
<p>重传应答机制与序列号的结合，可以处理数据可靠性传输的四个内容。</p>
<details class="folding-tag" blue><summary> 点击查看 TCP 如何处理可靠性传输的四个问题的 </summary>
              <div class='content'>
              <ul><li>处理数据在传输过程中被破坏的问题</li></ul><p>首先通过对所接收数据包的校验，确认该数据包中数据是否存在错误。如果有，则简单丢弃或者发送一个应答数据包重新对这些数据进行请求。发送端在等待一段时间后，则会重新发送这些数据。所以，<strong>数据传输错误的解决可以通过数据重传机制完成</strong>。</p><ul><li>处理接收重复数据问题</li></ul><p>首先利用序列号可以发现数据重复问题。因为每个传输的数据均被赋予一个唯一的序列号，如果到达的两份数据具有重叠的序列号（如由发送端数据包重传造成），则表示出现数据重复问题，此时只须丢弃其中一份保留另一份即可。多个数据包中数据重叠的情况解决方式类似。所以<strong>数据重复问题的解决可以通过检查序列号完成</strong>。</p><ul><li>发现数据丢失以及进行有效解决</li></ul><p>首先此处数据包丢失的概念是应该是指在<strong>一段合理时间内</strong>，理应到达的数据包没有到达，而非永远不到达。数据包丢失与数据包乱序到达有时在判断上和软件处理上很难区分，我们无法确定一个数据包在传输过程中就丢失，还是被延迟在网络中。而将二两者区分开来的一个<strong>主要依据是在合理的时间内</strong>。</p><p>如若接收端只接收到序列号从<code>1</code> 到<code>100</code> 的数据包，之后又接收到序列号从<code>200</code> 到<code>300</code> 的数据包，超出一段合理的时间后，序列号从<code>101</code> 到<code>199</code> 的数据一直未到达，则表示包含序列号从<code>101</code> 到<code>199</code> 的数据包在传输过程中很可能丢失（或者有极不正常的延迟）。</p><p>影响数据包是否丢失判断的另外一个干扰因素是发送端的重传机制，如果一个序列号较前的数据包在网络中丢失，造成序列号较后的数据包提前到达接收端，也会暂时造成序列号不连续，但由于发送端在没有接收到确认应答时，会重新发送序列号较前的那个数据包，如果此后接收端接收到一个重传的数据包，则仅仅只会在接收端造成数据包乱序到达的表象。</p><p>其实无论实质如何，如果软件实现判断出数据包丢失，则接收端就会不断发送对这些丢失的数据的请求数据包（也即应答数据包）来迫使发送端重新发送这些数据。通常发送端自身会自发的重传这些未得到对方确认的数据，但由于重传机制采用指数退避算法，每次重传的间隔时间均会加倍，所以通过发送方主动重传机制恢复的时间较长，而接收端通过不断发送对这些丢失数据的请求，发送端在接收到三个这样的请求数据包后（三个请求数据包中具有同一个请求序列号也即应答序列号），会立刻触发对这些数据的重新发送，这称为<strong>快速恢复或者快速重传机制</strong>。所以对于<strong>数据丢失问题的解决是可以通过数据重传机制完成的</strong>。</p><ul><li>处理接收端数据乱序到达问题</li></ul><p>如果通信双方存在多条传输路径， 则有可能出现数据乱序问题，即序列号较大的数据先于序列号较小的数据到达，而发送端确实是按序列号由小到大的顺序发送的。数据乱序意思是数据都成功到达了，但到达的顺序并未按照我们预想的那样。对这个问题的解决只需对这些数据进行重新排序即可。所以<strong>对数据乱序问题的解决可以通过排序数据序列号完成</strong>。</p>
              </div>
            </details>

<p>经过以上分析，我们可以知道<strong>序列号</strong>，<strong>数据超时重传</strong>和<strong>数据确认应答</strong>机制保证了 <code>TCP</code> 协议可靠性传输的要求。由于需要对所发送的数据进行编号，又需要对接收的数据进行应答，所以使用<code>TCP</code> 协议的通信双方必须通过某种机制了解对方的初始序列号。只有在确切知道对方的初始序列号的情况下，才能从一开始对所接收数据的合法性进行判断。另外还需要在本地维护一个对方应答的序列号，以随时跟随对方的数据请求。在最后通信通道关闭时，可以确知本地发送的数是否已被对方完全接收；此外这个对方应答序列号在控制本地数据通量方面也发挥着重要的作用：<strong>用本地发送序列号减去对方应答序列号则可以立刻得知目前发送出去的数据有多少没有得到对方的应答</strong>。</p>
<p>【注意】实际上，<code>TCP</code>的可靠性传输是非常复杂的，用了很多的机制来保证数据的可靠：重传机制、滑动窗口、流量控制以及阻塞控制，这里就不一一说明了，想知道的话，可以看笔记开篇的参考资料，里边的这一节介绍的非常详细：<a target="_blank" rel="noopener" href="https://xiaolincoding.com/network/3_tcp/tcp_feature.html">4.2 TCP 重传、滑动窗口、流量控制、拥塞控制 | 小林coding (xiaolincoding.com) <i class="fa fa-external-link-alt"></i></a></p>
<h2 id="6-应用场景"><a href="#6-应用场景" class="headerlink" title="6. 应用场景"></a><font size=3>6. 应用场景</font></h2><p>（1）适合于对传输质量要求较高，以及传输大量数据的通信。</p>
<p>（2）在需要可靠数据传输的场合，通常使用<code>TCP</code>协议。</p>
<p>（3）<code>MSN/QQ</code>等即时通讯软件的用户登录账户管理相关的功能通常采用<code>TCP</code>协议。</p>
<h1 id="二、TCP报文格式"><a href="#二、TCP报文格式" class="headerlink" title="二、TCP报文格式"></a><font size=3>二、<code>TCP</code>报文格式</font></h1><h2 id="1-报文图示"><a href="#1-报文图示" class="headerlink" title="1. 报文图示"></a><font size=3>1. 报文图示</font></h2><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623113754219.png" alt="image-20220623113754219" style="zoom:33%;" />

<p>【说明】</p>
<p>（1）序列号用来解决网络包乱序问题。</p>
<p>（2）确认应答号用来解决丢包的问题。</p>
<h2 id="2-各字段说明"><a href="#2-各字段说明" class="headerlink" title="2. 各字段说明"></a><font size=3>2. 各字段说明</font></h2><h3 id="2-1-源端口和目的端口字段"><a href="#2-1-源端口和目的端口字段" class="headerlink" title="2.1 源端口和目的端口字段"></a><font size=3>2.1 源端口和目的端口字段</font></h3><table>
    <tr><td align="center" width=150px>TCP源端口</td><td align="left">Source Port</td><td align="left">源计算机上的应用程序的端口号，占 16 位。</td></tr>
    <tr><td align="center" width=150px>TCP目的端口</td><td align="left">Destination Port</td><td align="left">目标计算机的应用程序端口号，占 16 位。</td></tr>
</table>

<h3 id="2-2-序列号字段"><a href="#2-2-序列号字段" class="headerlink" title="2.2 序列号字段"></a><font size=3>2.2 序列号字段</font></h3><p><code>TCP</code>序列号（<code>Sequence Number</code>），占 <code>32</code> 位，它表示本报文段所发送数据的第一个字节的编号。在 <code>TCP</code> 连接中，所传送的字节流的每一个字节都会按顺序编号。</p>
<p>当<code>SYN</code>标记不为<code>1</code>时，这是当前数据分段第一个字母的序列号；如果<code>SYN</code>的值是<code>1</code>时，这个字段的值就是初始序列值（<code>ISN</code>），用于对序列号进行同步。这时，第一个字节的序列号比这个字段的值大<code>1</code>，也就是<code>ISN</code>加<code>1</code>。</p>
<h3 id="2-3-确认应答字段"><a href="#2-3-确认应答字段" class="headerlink" title="2.3 确认应答字段"></a><font size=3>2.3 确认应答字段</font></h3><p><code>TCP</code> 确认应答号（<code>Acknowledgment Number</code>，<code>ACK Number</code>），占 <code>32</code> 位。它表示接收方期望收到发送方下一个报文段的第一个字节数据的编号。其值是接收计算机即将接收到的下一个序列号，也就是下一个接收到的字节的序列号加<code>1</code>。当<code>ACK</code>标志位为<code>1</code>时有效，表示期望收到的下一个报文段的第一个数据字节的序号。确认号为<code>N</code>，则表明到序号<code>N-1</code>为止的所有数据字节都已经被正确地接收到了。</p>
<h3 id="2-4-数据偏移字段"><a href="#2-4-数据偏移字段" class="headerlink" title="2.4 数据偏移字段"></a><font size=3>2.4 数据偏移字段</font></h3><p><code>TCP</code> 首部长度（<code>Header Length</code>）：数据偏移是指数据段中的数据部分起始处距离 <code>TCP</code> 数据段起始处的字节偏移量，占 <code>4</code>位。其实这里的数据偏移也是在确定 <code>TCP</code> 数据段头部分的长度，告诉接收端的应用程序，数据从何处开始。</p>
<h3 id="2-5-保留字段"><a href="#2-5-保留字段" class="headerlink" title="2.5 保留字段"></a><font size=3>2.5 保留字段</font></h3><p>保留（<code>Reserved</code>），占 <code>4</code> 位。为 <code>TCP</code> 将来的发展预留空间，目前必须置为 <code>0</code>。</p>
<h3 id="2-6-标志位字段"><a href="#2-6-标志位字段" class="headerlink" title="2.6 标志位字段"></a><font size=3>2.6 标志位字段</font></h3><table>
    <tr><td align="center" width=90px>CWR</td><td align="left">Congestion Window Reduce，拥塞窗口减少标志，用来表明它接收到了设置 ECE 标志的 TCP 包。并且，发送方收到消息之后，通过减小发送窗口的大小来降低发送速率。</td></tr>
    <tr><td align="center" width=90px>ECE</td><td align="left">ECN Echo，用来在 TCP 三次握手时表明一个 TCP 端是具备 ECN 功能的。在数据传输过程中，它也用来表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11，即网络线路拥堵。</td></tr>
    <tr><td align="center" width=90px>URG</td><td align="left">Urgent，表示本报文段中发送的数据是否包含紧急数据。URG=1 时表示有紧急数据。当 URG=1 时，后面的紧急指针字段才有效。</td></tr>
    <tr><td align="center" width=90px>ACK</td><td align="left">表示前面的确认号字段是否有效。ACK=1 时表示有效。只有当 ACK=1 时，前面的确认号字段才有效。TCP 规定，连接建立后，ACK 必须为 1。</td></tr>
    <tr><td align="center" width=90px>PSH</td><td align="left">Push，告诉对方收到该报文段后是否立即把数据推送给上层。如果值为 1，表示应当立即把数据提交给上层，而不是缓存起来。</td></tr>
    <tr><td align="center" width=90px>RST</td><td align="left">表示是否重置连接。如果 RST=1，说明 TCP 连接出现了严重错误（如主机崩溃），必须释放连接，然后再重新建立连接。</td></tr>
    <tr><td align="center" width=90px>SYN</td><td align="left">在建立连接时使用，用来同步序号。当 SYN=1，ACK=0 时，表示这是一个请求建立连接的报文段；当 SYN=1，ACK=1 时，表示对方同意建立连接。SYN=1 时，说明这是一个请求建立连接或同意建立连接的报文。只有在前两次握手中 SYN 才为 1。</td></tr>
    <tr><td align="center" width=90px>FIN</td><td align="left">标记数据是否发送完毕。如果 FIN=1，表示数据已经发送完成，可以释放连接。</td></tr>
</table>

<h3 id="2-7-窗口大小字段"><a href="#2-7-窗口大小字段" class="headerlink" title="2.7 窗口大小字段"></a><font size=3>2.7 窗口大小字段</font></h3><p>窗口大小（<code>Window Size</code>），占 <code>16</code> 位。它表示从 <code>Ack Number</code> 开始还可以接收多少字节的数据量，也表示当前接收端的接收窗口还有多少剩余空间。该字段可以用于 <code>TCP</code> 的流量控制。</p>
<h3 id="2-8-TCP校验和字段"><a href="#2-8-TCP校验和字段" class="headerlink" title="2.8 TCP校验和字段"></a><font size=3>2.8 TCP校验和字段</font></h3><p>校验位（<code>TCP Checksum</code>），占 <code>16</code> 位。它用于确认传输的数据是否有损坏。发送端基于数据内容校验生成一个数值，接收端根据接收的数据校验生成一个值。两个值必须相同，才能证明数据是有效的。如果两个值不同，则丢掉这个数据包。<code>Checksum</code> 是根据伪头 + <code>TCP</code> 头 +<code> TCP</code> 数据三部分进行计算的。</p>
<h3 id="2-9-紧急指针字段"><a href="#2-9-紧急指针字段" class="headerlink" title="2.9 紧急指针字段"></a><font size=3>2.9 紧急指针字段</font></h3><p>紧急指针（<code>Urgent Pointer</code>），仅当前面的 <code>URG</code> 控制位为 <code>1</code> 时才有意义。它指出本数据段中为紧急数据的字节数，占 <code>16 </code>位。当所有紧急数据处理完后，<code>TCP</code> 就会告诉应用程序恢复到正常操作。即使当前窗口大小为<code>0</code>，也是可以发送紧急数据的，因为紧急数据无须缓存。</p>
<h1 id="三、TCP建立连接"><a href="#三、TCP建立连接" class="headerlink" title="三、TCP建立连接"></a><font size=3>三、<code>TCP</code>建立连接</font></h1><h2 id="1-三次握手过程"><a href="#1-三次握手过程" class="headerlink" title="1. 三次握手过程"></a><font size=3>1. 三次握手过程</font></h2><p><code>TCP</code> 是面向连接的协议，所以每次发出的请求都需要对方进行确认。<code>TCP</code> 客户端与 <code>TCP</code> 服务器在通信之前需要完成三次握手才能建立连接。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623120707723.png" alt="image-20220623120707723" style="zoom:50%;" />

<p>一开始，客户端和服务端都处于 <code>CLOSED</code> 状态。首先客户端主动打开连接，然后服务端被动监听某个端口，处于 <code>LISTEN</code> 状态。接下来就可以开始客户端与服务端的连接过程了。由于前边已经安装过抓包工具<code>wireshark</code>了，并且此篇笔记是网络编程学习完回顾时所写，这里使用后边编写的<code>TCP</code>本地服务端和客户端连接实例来实际抓一下包，看一下这三次连接过程。</p>
<details class="folding-tag" blue><summary> 点击查看操作过程 </summary>
              <div class='content'>
              <p>在<code>ubuntu</code>中，使用以下命令打开<code>wireshark</code>：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo wireshark</span><br></pre></td></tr></table></figure><p>然后进入<code>wireshark</code>软件，接口选择<code>loopback:lo</code>，额，这里吧，我暂时还不是很懂，只知道选择这个才可以捕获到本地自己编写的<code>TCP</code>服务端和客户端连接过程，后边再补充吧。</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623141216063.png" alt="image-20220623141216063" style="zoom: 50%;" /><p>然后我们另开两个终端吗，分别运行<code>TCP</code>服务器端和客户端，当客户端连接上服务器的时候，会有以下数据被抓取（还可能有其他的包，只是这里使用了显示过滤器，只显示<code>TCP</code>包）：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623142905506.png" alt="image-20220623142905506" style="zoom:50%;" /><p>然后我们在客户端输入退出标记，然后断开<code>TCP</code>连接，会再得到几个<code>TCP</code>的数据包：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623142923675.png" alt="image-20220623142923675" style="zoom:50%;" /><p>可能是电脑分辨率的问题，在<code>Ubuntu</code>中字体实在太小，我们保存文件到<code>Windows</code>下，再打开分析也是一样的。</p>
              </div>
            </details>

<ul>
<li><strong>第一次握手</strong>：<code>TCP</code>客户端向服务器端发出建立连接请求的报文段</li>
</ul>
<p>客户端会随机初始化一个序列号<code>seq</code>（值为<code>X</code>），并将此序列号置于 <code>TCP</code> 首部的序列号字段中，同时把同步位 <code>SYN</code> 标志位置为 <code>1</code> ，表示 这是一个<code>SYN</code> 报文。接着把第一个 <code>SYN</code> 报文发送给服务端，表示向服务端发起连接请求，该报文不包含应用层数据，客户端发送完请求报文之后，客户端会处于 <code>SYN-SENT</code> （同步已发送）状态。</p>
<p><code>TCP</code>规定，当报文段的<code>SYN=1</code>且<code>ACK=0</code>（可以查看上边<code>ACK</code>标志取值的情况，不过这里是建立连接请求，不一定必须是<code>1</code>）时，表明这是一个请求建立连接的<code>SYN</code>报文段（<code>SYN=1</code>的报文段）不能携带数据，但是要消耗掉一个序列号。</p>
<p>我们实际抓取到的<code>TCP</code>的包如下图所示：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143444725.png" alt="image-20220623143444725" style="zoom:33%;" />

<ul>
<li><strong>第二次握手</strong>：服务器向客户端发回确认报文段</li>
</ul>
<p>服务端收到客户端的 <code>SYN</code> 报文后，首先服务端也随机初始化自己的序列号<code>seq</code>（值为<code>y</code>），并将此序列号填入 <code>TCP</code> 首部的序列号字段中。其次把确认号<code>ack</code>（值为<code>x+1</code>）填入 <code>TCP</code> 首部的确认应答号字段, 接着把 <code>SYN</code> 和 <code>ACK</code> 标志位置为 <code>1</code>，最后把该报文发给客户端。该报文也不包含应用层数据，服务器端发送完确认报文之后，服务端会处于 <code>SYN-RCVD</code> (同步收到)状态。</p>
<p><code>TCP</code>规定，当报文段的<code>SYN=1</code>且<code>ACK=1</code>时，表明这是一个同一建立连接响应报文段；这个报文段也不能携带数据，同样需要消耗掉一个序列号。</p>
<p>我们实际抓取到的<code>TCP</code>的包如下图所示：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143622193.png" alt="image-20220623143622193" style="zoom:33%;" />

<ul>
<li><strong>第三次握手</strong>：<code>TCP</code>客户端收到确认后，还要再向服务器发出确认报文段</li>
</ul>
<p>客户端收到服务端报文后，还要向服务端回应最后一个应答报文，首先将该应答报文 <code>TCP</code> 首部 <code>ACK</code> 标志位置为 <code>1</code> ，并将确认号<code>ack</code>（值为<code>y+1</code>）填入确认应答号字段 ，然后把报文发送给服务端，这次报文可以携带客户到服务器的数据，之后客户端处于 <code>ESTABLISHED</code>（已建立连接） 状态。</p>
<p><code>TCP</code>规定，这个<code>ACK</code>报文段可以携带数据；如果不携带数据则不消耗序号，客户端的下一个数据报文段的序号仍然是<code>seq=x+1</code>。</p>
<p>我们实际抓取到的<code>TCP</code>的包如下图所示：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623143730304.png" alt="image-20220623143730304" style="zoom:33%;" />

<p>经过以上过程，客户端就和服务端建立连接了，建立连接后，两者都处于 <code>ESTABLISHED</code> （已建立连接）状态。同时双方都得到了彼此的窗口大小，序列号等信息，在传输 TCP 报文段的时候，每个 TCP 报文段首部的 SYN 标志都会被置 0，因为它只用于发起连接，同步序号。  </p>
<p>其实简单来说就是这么一个过程，小明和小红打电话：</p>
<p>小明：“小红小红，我是小明，你能听见我说话吗？”——小明要确认小红是否可以听到自己说话</p>
<p>小红：“小明小明，我是小红，我能听见你说话，你能听见我说话吗？”——小红确认自己可以听到小明说话，同时需要确认小明是否可以听见自己说话。</p>
<p>小明：“小红小红，我能听见说话哦。”——小明告诉小红自己可以听到小红说话。</p>
<p>然后。。。。。然后就开始愉快的聊天啦。</p>
<blockquote>
<p>在Linux中查看TCP的状态：</p>
<p>我们可以在终端中执行以下命令：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -napt</span><br></pre></td></tr></table></figure>


</blockquote>
<p>【注意】</p>
<p>（1）经过上边的分析过程我们知道，<code>seq</code>应该是一个随机值才对，为什么抓包的<code>seq</code>是从<code>0</code>开始的？这是因为软件帮我们做了优化处理，要想显示真正的序列号，我们可以在数据包详情面板中这样操作：【选中<code>Transmission Control Protocol</code>】&rarr;【右键】&rarr;【协议首选项】&rarr;【<code>Relative sequence numbers</code>】，然后去掉前边的勾就可以啦。</p>
<p>（2）上面写的 ack 和 ACK，不是同一个概念：小写的 ack 代表的是头部的确认号 Acknowledge number， ack。大写的 ACK，则是 TCP 首部的标志位，用于标志的 TCP 包是否对上一个包进行了确认操作，如果确认了，则把 ACK 标志位设置成 1。  </p>
<h2 id="2-为什么是三次握手？"><a href="#2-为什么是三次握手？" class="headerlink" title="2. 为什么是三次握手？"></a><font size=3>2. 为什么是三次握手？</font></h2><blockquote>
<p>为啥需要三次握手，两次不行吗？</p>
</blockquote>
<ul>
<li>第一次握手：客户端发送网络包，服务端收到了。这样服务端就能得出结论：客户端的发送能力、服务端的接收能力是正常的。</li>
<li>第二次握手：服务端发包，客户端收到了。这样客户端就能得出结论：服务端的接收、发送能力，客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。</li>
<li>第三次握手：客户端发包，服务端收到了。这样服务端就能得出结论：客户端的接收、发送能力正常，服务器自己的发送、接收能力也正常。</li>
</ul>
<p>假如客户端发出了连接请求，但因为网络波动导致服务器并没有收到来自客户端的请求连接，于是客户端又重发了一次连接请求，客户端和服务器经过两次握手就建立好连接。双方开始传输数据，数据传输完成以后，双方断开连接。过一段时间后，原本在网络传输中搁置的连接请求到达了服务器。服务器以为是客户端又发出来一次新的连接请求，于是就向客户端发送确认报文段，同意建立连接(两次握手只需要服务器发出确认报文段，就建立好连接)。此时服务器一直在等待客户端发送的数据，一直浪费着系统资源。因此，需要三次握手才能<strong>确认双方的接收与发送能力是否正常</strong>。</p>
<blockquote>
<p>那四次握手可以嘛？当然就可以啦，但是有些多余，还会浪费资源。</p>
</blockquote>
<p><code>TCP</code>三次握手原本应该是四次握手，但是中间的同步报文段<code>SYN</code>和应答报文<code>ACK</code>是可以合在一起的，这两个操作在时间上是同时发送的。当客户端的到达同步报文段<code>SYN</code>到达服务器的时候，服务器的内核就会第一时间进行应答报文段<code>ACK</code>， 同时也会第一时间发起同步报文段<code>SYN</code>,这两件事情同时触发，于是就没必要分成两次传输，直接一步到位。分成两次反而会更浪费系统资源(需要进行两次的封装和分用)。</p>
<blockquote>
<p>【总结】三次握手的原因</p>
<ul>
<li>三次握手才可以阻止重复历史连接的初始化（主要原因）;</li>
<li>三次握手才可以同步双方的初始序列号;</li>
<li>三次握手才可以避免资源浪费;</li>
</ul>
</blockquote>
<h2 id="3-两个重要的序列号"><a href="#3-两个重要的序列号" class="headerlink" title="3. 两个重要的序列号"></a><font size=3>3. 两个重要的序列号</font></h2><h3 id="3-1-初始序列号"><a href="#3-1-初始序列号" class="headerlink" title="3.1 初始序列号"></a><font size=3>3.1 初始序列号</font></h3><p><code>ISN</code>(<code>Initial Sequence Number</code>)，即初始序列号，在上边的图中我使用<code>seq</code>表示了。序列号是按顺序给发送数据的每一个字节都标上号码的编号。当一端为建立连接而发送它的<code>SYN</code>时，它会为连接选择一个初始的序列号<code>ISN</code>(客户端和服务器会分别选择一个初始序列号<code>ISN</code>)。初始序列号<code>ISN</code>并非为<code>0</code>，而是由随机数生成，所以每次初始的序列号可能都不一样，并不固定，但是一旦初始序列号生成，那么后面的计算则是对每一字节加一。</p>
<p>三次握手的其中一个重要功能是客户端和服务端交换初始序列号<code>ISN</code>，以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果初始序列号 <code>ISN</code> 是固定的，攻击者很容易猜出后续的确认号，从而打断正常的<code>TCP</code>连接，因此 <code>ISN</code> 是动态生成的。</p>
<p>那这个<code>ISN</code>是如何生成的呢？起始 <code>ISN</code> 是基于时钟的，每 <code>4</code> 微秒 +<code>1</code>，转一圈要 <code>4.55</code> 个小时。<code>RFC793</code> 提到初始化序列号 <code>ISN </code>随机生成算法：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ISN = M + F(localhost, localport, remotehost, remoteport)</span><br></pre></td></tr></table></figure>

<ul>
<li><code>M</code> 是一个计时器，这个计时器每隔 <code>4</code> 微秒加 <code>1</code>。</li>
<li><code>F</code> 是一个 <code>Hash</code> 算法，根据源 <code>IP</code>、目的 <code>IP</code>、源端口、目的端口生成一个随机数值。要保证 <code>Hash</code> 算法不能被外部轻易推算得出，用 <code>MD5</code> 算法是一个比较好的选择。</li>
</ul>
<p>可以看到，随机数是会基于时钟计时器递增的，基本不可能会随机成一样的初始化序列号。</p>
<h3 id="3-2-应答序列号"><a href="#3-2-应答序列号" class="headerlink" title="3.2 应答序列号"></a><font size=3>3.2 应答序列号</font></h3><p>在服务端进行回应的时候发送的数据确认应答数据包会有一个应答序列号，它有什么意义呢？</p>
<p>应答序列号并非其表面上所显示的意义，其<strong>实际上是指接收端希望接收的下一个字节的序列号</strong>。所以接收端在成功接收到部分数据后，其发送的应答数据包中应答序列号被设置为这些数据中最后一个字节的序列号加一。所以从其含义上来说，应答序列号称为请求序列号有时更为合适。应答序列号在<code>TCP</code> 首部中应答序列号字段中被设置。而<code>TCP</code> 首部中序列号字段表示包含该<code>TCP</code> 首部的数据包中所包含数据的第一个字节的序列号（令为<code>N</code>）。如果接收端成功接收该数据包，之前又无丢失数据包，则接收端发送的应答数据包中的应答序列号应该为：<code>N+LEN</code>。其中<code>LEN</code> 为接收的数据包的数据长度。该应答序列号也是发送端将要发送的下一个数据包中第一个字节的序列号（由此亦可看出可以将应答序列号称为请求序列号的原因所在）。</p>
<h2 id="4-握手可以携带数据吗？"><a href="#4-握手可以携带数据吗？" class="headerlink" title="4. 握手可以携带数据吗？"></a><font size=3>4. 握手可以携带数据吗？</font></h2><p>第一次、第二次不可以，第三次可以。</p>
<p>如果第一次握手可以携带数据的话，那么将会使服务器更容易遭受攻击。如果第一次握手携带就大量的数据，那么服务器需要花很长的时间才能对数据进行解析。如果进行重复的发送，那么服务器就会因为系统资源殆尽而崩溃。</p>
<p>第三次握手可以携带数据。第三次握手时客户端处于<code>ESTABLISH</code>状态。对于客户端来说，它已经建立好了连接，并且它已经知道服务器的接收能力和自己的发送能力是正常的，所以可以进行正常的发送数据。</p>
<h2 id="5-TCP握手丢失？"><a href="#5-TCP握手丢失？" class="headerlink" title="5. TCP握手丢失？"></a><font size=3>5. <code>TCP</code>握手丢失？</font></h2><h3 id="5-1-第一次握手丢失？"><a href="#5-1-第一次握手丢失？" class="headerlink" title="5.1 第一次握手丢失？"></a><font size=3>5.1 第一次握手丢失？</font></h3><p>当客户端想和服务端建立 <code>TCP</code> 连接的时候，首先第一个发的就是 <code>SYN</code> 报文，然后进入到 <code>SYN_SENT</code> 状态。在这之后，如果客户端迟迟收不到服务端的 <code>SYN-ACK</code> 报文（第二次握手），就会触发<strong>超时重传机制</strong>，重传 <code>SYN</code> 报文。</p>
<p>不同版本的操作系统可能超时时间不同，有的 <code>1</code> 秒的，也有 <code>3</code> 秒的，这个超时时间是写死在内核里的，如果想要更改则需要重新编译内核，比较麻烦。</p>
<p>当客户端在 <code>1</code> 秒后没收到服务端的 <code>SYN-ACK</code> 报文后，客户端就会重发 <code>SYN</code> 报文，那到底重发几次呢？在 <code>Linux</code> 里，客户端的 <code>SYN</code> 报文最大重传次数由 <code>tcp_syn_retries</code>内核参数控制，这个参数是可以自定义的，默认值一般是 <code>5</code>。</p>
<p>通常，第一次超时重传是在 <code>1</code> 秒后，第二次超时重传是在 <code>2</code> 秒，第三次超时重传是在 <code>4</code> 秒后，第四次超时重传是在 <code>8</code> 秒后，第五次是在超时重传 <code>16</code> 秒后。<strong>每次超时的时间是上一次的 2 倍</strong>。当第五次超时重传后，会继续等待 <code>32</code> 秒，如果服务端仍然没有回应 <code>ACK</code>，客户端就不再发送 <code>SYN</code> 包，然后断开 <code>TCP</code> 连接。所以，总耗时是 <code>1+2+4+8+16+32=63</code> 秒，大约<code>1</code>分钟左右。</p>
<h3 id="5-2-第二次握手丢失？"><a href="#5-2-第二次握手丢失？" class="headerlink" title="5.2 第二次握手丢失？"></a><font size=3>5.2 第二次握手丢失？</font></h3><p>当服务端收到客户端的第一次握手后，就会回 <code>SYN-ACK</code> 报文给客户端，这个就是第二次握手，此时服务端会进入 <code>SYN_RCVD</code> 状态。</p>
<p>第二次握手的 <code>SYN-ACK</code> 报文其实有两个目的 ：</p>
<ul>
<li>第二次握手里的 <code>ACK</code>， 是对第一次握手的确认报文；</li>
<li>第二次握手里的 <code>SYN</code>，是服务端发起建立 <code>TCP</code> 连接的报文；</li>
</ul>
<p>所以，如果第二次握手丢了，具体会怎么样呢？</p>
<p>一方面，因为第二次握手报文里是包含对客户端的第一次握手的 <code>ACK</code> 确认报文，所以，如果客户端迟迟没有收到第二次握手，那么客户端就觉得可能自己的 <code>SYN</code> 报文（第一次握手）丢失了，于是<strong>客户端就会触发超时重传机制，重传 SYN 报文</strong>。</p>
<p>另一方面，因为第二次握手中包含服务端的 <code>SYN</code> 报文，所以当客户端收到后，需要给服务端发送 <code>ACK</code> 确认报文（第三次握手），服务端才会认为该 <code>SYN</code> 报文被客户端收到了。如果第二次握手丢失了，服务端就收不到第三次握手，于是<strong>服务端这边会触发超时重传机制，重传 SYN-ACK 报文</strong>。</p>
<p>在 <code>Linux</code> 下，<code>SYN-ACK</code> 报文的最大重传次数由 <code>tcp_synack_retries</code>内核参数决定，默认值是 <code>5</code>。因此，当第二次握手丢失了，客户端和服务端都会重传：</p>
<ul>
<li>客户端会重传 <code>SYN</code> 报文，也就是第一次握手，最大重传次数由 <code>tcp_syn_retries</code>内核参数决定；</li>
<li>服务端会重传 <code>SYN-ACK</code> 报文，也就是第二次握手，最大重传次数由 <code>tcp_synack_retries</code> 内核参数决定。</li>
</ul>
<h3 id="5-3-第三次握手丢失？"><a href="#5-3-第三次握手丢失？" class="headerlink" title="5.3 第三次握手丢失？"></a><font size=3>5.3 第三次握手丢失？</font></h3><p>客户端收到服务端的 <code>SYN-ACK</code> 报文后，就会给服务端回一个 <code>ACK</code> 报文，也就是第三次握手，此时客户端状态进入到 <code>ESTABLISH</code> 状态。因为这个第三次握手的 <code>ACK</code> 是对第二次握手的 <code>SYN</code> 的确认报文，所以当第三次握手丢失了，如果服务端那一方迟迟收不到这个确认报文，就会触发超<strong>时重传机制</strong>，重传 <code>SYN-ACK</code> 报文，直到收到第三次握手，或者达到最大重传次数。</p>
<p><strong>【注意】</strong><code>ACK</code> 报文是不会有重传的，当 <code>ACK</code> 丢失了，就由对方重传对应的报文。</p>
<h1 id="四、TCP连接断开"><a href="#四、TCP连接断开" class="headerlink" title="四、TCP连接断开"></a><font size=3>四、<code>TCP</code>连接断开</font></h1><h2 id="1-四次挥手过程"><a href="#1-四次挥手过程" class="headerlink" title="1. 四次挥手过程"></a><font size=3>1. 四次挥手过程</font></h2><p><code>TCP</code> 断开连接是通过<strong>四次挥手</strong>方式，需要客户端和服务端总共发送 4 个包以确认连接的断开。在 socket 编程中，这一过程由客户端或服务端任一方执行 close 来触发。</p>
<p>由于 TCP 连接是全双工的，因此，每个方向都必须要单独进行关闭，这一原则是当一方完成数据发送任务后，发送一个 FIN 来终止这一方向的连接，收到一个 FIN 只是意味着这一方向上没有数据流动了，即不会再收到数据了，但是在这个 TCP 连接上仍然能够发送数据，直到这一方向也发送了 FIN。首先进行关闭的一方将执行主动关闭，而另一方则执行被动关闭。  </p>
<p>双方都可以主动断开连接，断开连接后主机中的资源将被释放，四次挥手的过程如下图：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20220623150318856.png" alt="image-20220623150318856" style="zoom:50%;" />

<p>下边分析一下四次握手的过程，这里就不再使用<code>TCP</code>数据包实际分析了，图片太多的话，云存储的流量顶不住啊，不过有了上边三次握手建立连接的分析，四次握手断开连接也是很容易理解的。</p>
<ul>
<li><strong>第一次挥手</strong>：<code>TCP</code>客户进程向服务器端发出释放连接请求报文段</li>
</ul>
<p>如上图所示，释放连接的报文段中<code>FIN</code>（终止位）需要置<code>1</code>，并初始化一个序列号<code>seq</code>，设<code>seq=u</code>，并将此序列号填入 <code>TCP</code> 首部的序列号字段中。客户端发送完释放连接的请求报文后，客户端进程进入<code>FIN-WAIT-1</code>（终止等待<code>1</code>）状态。</p>
<p><code>TCP</code>规定，当报文段的<code>FIN=1</code>时，表明此报文段的发送方的数据已发生完毕，并要求释放连接；<code>FIN</code>报文段（<code>FIN=1</code>的报文段）不能携带数据，但是要消耗掉一个序列号。</p>
<ul>
<li><strong>第二次挥手</strong>：<code>TCP</code>服务器端向客户端发送确认报文</li>
</ul>
<p>确认报文中，<code>TCP</code>服务器端也会初始化一个序列号<code>seq</code>，设<code>seq=y</code>，并将此序列号填入 <code>TCP</code> 首部的序列号字段中。其次把确认号<code>ack</code>（值为<code>x+1</code>）填入 <code>TCP</code> 首部的确认应答号字段。接着把 <code>ACK</code> 标志位置为 <code>1</code>，最后把该报文发给客户端，发送完毕之后，服务端进程进入<code>CLOSE-WAIT</code>（关闭等待）状态。</p>
<p><code>TCP</code>服务器进程这时应该通知高层应用经常，客户端到服务器端这个方向的连接就要释放了，这时的<code>TCP</code>连接处于半关闭（<code>half-close</code>）状态。</p>
<p>客户端在收到服务器端的确认报文段之后，就进入<code>FIN-WAIT-2</code>（终止等待<code>2</code>）状态，等待服务器发出的连接释放报文段。</p>
<ul>
<li><strong>第三次挥手</strong>：服务器端向客户端发送连接释放报文段</li>
</ul>
<p>若服务器端已经没有要向客户端发送的数据了，那么服务器端的应用进程就通知<code>TCP</code>释放连接，此时会再初始化一个<code>seq</code>序列号，设<code>seq=z</code>，并将此序列号填入 <code>TCP</code> 首部的序列号字段中。其次把确认号<code>ack</code>（值为<code>x+1</code>）填入 <code>TCP</code> 首部的确认应答号字段。,接着把 <code>ACK</code> 标志位和<code>FIN</code>标志位置为 <code>1</code>，最后把该连接释放报文发给客户端，发送完毕之后，服务端进程进入<code>LAST-ACK</code>（最后确认）状态。</p>
<p><code>TCP</code>规定，当报文段的<code>FIN=1</code>时，表明此报文段的发送方的数据已发生完毕，并要求释放连接；<code>FIN</code>报文段（<code>FIN=1</code>的报文段）不能携带数据，但是要消耗掉一个序列号。</p>
<ul>
<li><strong>第四次挥手</strong>：客户端向服务器端发出确认报文段</li>
</ul>
<p>客户端收到服务器端发送的连接释放报文段后，必须对此发出一个确认报文段。此时将<code>seq=x+1</code>填入 <code>TCP</code> 首部的序列号字段中，将<code>ack=z+1</code>填入 <code>TCP</code> 首部的确认应答号字段。接着把 <code>ACK</code> 标志位置为 <code>1</code>，最后把该报文发给服务器端，客户端发送完毕确认报文段后，进入到<code>TIME-WAIT</code>（时间等待）状态。服务器收到了 <code>ACK</code> 应答报文后，就进入了 <code>CLOSED</code> 状态，至此服务端已经完成连接的关闭。</p>
<p>随后，客户端在经过 <code>2MSL</code> 一段时间后，自动进入 <code>CLOSED</code> 状态，至此客户端也完成连接的关闭。</p>
<p>有上边的分析过程可知，每个方向都需要一个 <code>FIN</code> 和一个 <code>ACK</code>，因此通常被称为四次挥手。</p>
<h2 id="2-为什么是四次挥手？"><a href="#2-为什么是四次挥手？" class="headerlink" title="2. 为什么是四次挥手？"></a><font size=3>2. 为什么是四次挥手？</font></h2><p>原因主要有以下两点：</p>
<ul>
<li>关闭连接时，客户端向服务端发送 <code>FIN</code> 时，仅仅表示客户端不再发送数据了但是还能接收数据。</li>
<li>服务器收到客户端的 <code>FIN</code> 报文时，先回一个 <code>ACK</code> 应答报文，而服务端可能还有数据需要处理和发送，等服务端不再发送数据时，才发送 <code>FIN</code> 报文给客户端来表示同意现在关闭连接。</li>
</ul>
<p>由于服务端通常需要等待完成数据的发送和处理，所以服务端的 <code>ACK</code> 和 <code>FIN</code> 一般都会分开发送，从而比三次握手导致多了一次。</p>
<h2 id="3-挥手丢失？"><a href="#3-挥手丢失？" class="headerlink" title="3. 挥手丢失？"></a><font size=3>3. 挥手丢失？</font></h2><h3 id="3-1-第一次挥手丢失？"><a href="#3-1-第一次挥手丢失？" class="headerlink" title="3.1 第一次挥手丢失？"></a><font size=3>3.1 第一次挥手丢失？</font></h3><p>当客户端（主动关闭方）调用 <code>close</code> 函数后，就会向服务端发送<code>FIN</code>报文，也就是请求释放连接的报文，试图与服务端断开连接，此时客户端的连接进入到 <code>FIN_WAIT_1</code> 状态。</p>
<p>正常情况下，如果能及时收到服务端（被动关闭方）的 <code>ACK</code>，则会很快变为 <code>FIN_WAIT2</code>状态。如果第一次挥手丢失了，那么客户端迟迟收不到被动方的 <code>ACK</code> 的话，也就会触发超时重传机制，重传 <code>FIN</code> 报文，重发次数由 <code>tcp_orphan_retries</code> 参数控制。当客户端重传 <code>FIN</code> 报文的次数超过 <code>tcp_orphan_retries</code> 后，就不再发送 <code>FIN</code> 报文，直接进入到 <code>close</code> 状态。</p>
<h3 id="3-2-第二次挥手丢失？"><a href="#3-2-第二次挥手丢失？" class="headerlink" title="3.2 第二次挥手丢失？"></a><font size=3>3.2 第二次挥手丢失？</font></h3><p>当服务端收到客户端的第一次挥手后，就会先回一个 <code>ACK</code> 确认报文，此时服务端的连接进入到 <code>CLOSE_WAIT</code> 状态。</p>
<p><code>ACK</code> 报文是不会重传的（这一句其实我是有疑问的，在网上看了两篇文章，都有这句话，但是我不是特别理解，应该是与<code>TCP</code>的重传机制有关，后边懂了再补充吧），所以如果服务端的第二次挥手丢失了，客户端就会触发超时重传机制，重传 <code>FIN</code> 报文，直到收到服务端的第二次挥手，或者达到最大的重传次数。</p>
<p>需要注意的是，当客户端收到第二次挥手，也就是收到服务端发送的 <code>ACK</code> 报文后，客户端就会处于 <code>FIN_WAIT2</code> 状态，在这个状态需要等服务端发送第三次挥手，也就是服务端的 <code>FIN</code> 报文。</p>
<p>对于 <code>close</code> 函数关闭的连接，由于无法再发送和接收数据，所以<code>FIN_WAIT2</code> 状态不可以持续太久，而 <code>tcp_fin_timeout</code> 控制了这个状态下连接的持续时长，默认值是 <code>60</code> 秒。这意味着对于调用 <code>close</code> 关闭的连接，如果在 <code>60</code> 秒后还没有收到 <code>FIN</code> 报文，客户端（主动关闭方）的连接就会直接关闭。</p>
<p>这里要注意，如果主动关闭方使用 <code>shutdown</code> 函数关闭连接且指定只关闭发送方向，而接收方向并没有关闭，那么意味着主动关闭方还是可以接收数据的。如果主动关闭方一直没收到第三次挥手，那么主动关闭方的连接将会一直处于 <code>FIN_WAIT2</code> 状态（<code>tcp_fin_timeout</code> 是无法控制 <code>shutdown</code> 关闭的连接的）。</p>
<h3 id="3-3-第三次挥手丢失？"><a href="#3-3-第三次挥手丢失？" class="headerlink" title="3.3 第三次挥手丢失？"></a><font size=3>3.3 第三次挥手丢失？</font></h3><p>当服务端（被动关闭方）收到客户端（主动关闭方）的 <code>FIN</code> 报文后，内核会自动回复 <code>ACK</code>，同时连接处于 <code>CLOSE_WAIT</code> 状态，它表示等待应用进程调用 <code>close</code> 函数关闭连接。此时，内核是没有权利替代进程关闭连接，必须由进程主动调用 <code>close</code> 函数来触发服务端发送 <code>FIN</code> 报文。</p>
<p>服务端处于 <code>CLOSE_WAIT</code> 状态时，调用了 <code>close</code> 函数，内核就会发出 <code>FIN</code> 报文，同时连接进入 <code>LAST_ACK</code> 状态，等待客户端返回 <code>ACK</code> 来确认连接关闭。如果迟迟收不到这个 <code>ACK</code>，服务端就会重发 <code>FIN</code> 报文，重发次数仍然由 <code>tcp_orphan_retrie</code>s 参数控制，这与客户端重发 <code>FIN</code> 报文的重传次数控制方式是一样的。</p>
<h3 id="3-4-第四次挥手丢失？"><a href="#3-4-第四次挥手丢失？" class="headerlink" title="3.4 第四次挥手丢失？"></a><font size=3>3.4 第四次挥手丢失？</font></h3><p>当客户端收到服务端的第三次挥手的 <code>FIN</code> 报文后，就会回 <code>ACK</code> 报文，也就是第四次挥手，此时客户端连接进入 <code>TIME_WAIT</code> 状态。在 <code>Linux</code> 系统，<code>TIME_WAIT</code> 状态会持续 <code>2MSL</code> 后才会进入关闭状态。</p>
<p>然后，服务端（被动关闭方）没有收到 <code>ACK</code> 报文前，还是处于 <code>LAST_ACK</code> 状态。如果第四次挥手的 <code>ACK</code> 报文没有到达服务端，服务端就会重发 <code>FIN</code> 报文，重发次数仍然由前面介绍过的 <code>tcp_orphan_retries</code> 参数控制。</p>
<h2 id="4-2MSL具体是什么？"><a href="#4-2MSL具体是什么？" class="headerlink" title="4. 2MSL具体是什么？"></a><font size=3>4. <code>2MSL</code>具体是什么？</font></h2><p>上边有个疑问，就是为什么 <code>TIME_WAIT</code> 等待的时间是 <code>2MSL</code>？<code>2MSL</code>到底是多久类？</p>
<p><code>MSL</code> 是 <code>Maximum Segment Lifetime</code>，就是<strong>报文最大生存时间</strong>，它是任何报文在网络上存在的最长时间，超过这个时间报文将被丢弃。因为 <code>TCP</code> 报文是基于 <code>IP</code> 协议的，而 <code>IP</code> 头中有一个 <code>TTL</code> 字段，是 <code>IP</code> 数据报可以经过的最大路由数，每经过一个处理他的路由器此值就减 <code>1</code>，当此值为 <code>0 </code>则数据报将被丢弃，同时发送 <code>ICMP</code> 报文通知源主机。</p>
<p><code>MSL</code> 与 <code>TTL</code> 的区别是 <code>MSL</code> 的单位是时间，而 <code>TTL</code> 是经过路由跳数。所以 <code>MSL</code> 应该要大于等于 <code>TTL</code> 消耗为 <code>0</code> 的时间，以确保报文已被自然消亡。<code>TTL</code> 的值一般是 <code>64</code>，<code>Linux</code> 将 <code>MSL</code> 设置为 <code>30</code> 秒，意味着 <code>Linux</code> 认为数据报文经过 <code>64</code> 个路由器的时间不会超过 <code>30</code> 秒，如果超过了，就认为报文已经消失在网络中了。</p>
<p>其实客户端在发送最后一次确认报文段之后还需要等待<code>2MSL</code>时间，主要有两个原因：</p>
<ul>
<li><p>（1）为了保证客户端发送的最后一个<code>ACK</code>报文段能够到达服务器端。因为这个<code>ACK</code>报文段有可能丢失，这样服务器端就无法接收到而进入<code>CLOSED</code>状态。于是服务器端会重传请求释放连接的报文段，客户端在这段等待时间接收到了，重传<code>ACK</code>报文段，这样服务器端还是会顺利接收到确认报文段，进入<code>CLOSED</code>状态。如果此时客户端已经关闭了，那么就无法收到服务器端的请求报文段，也不会发送<code>ACK</code>报文段，这样服务器端就无法进入<code>CLOSED</code>状态了。</p>
</li>
<li><p>（2）在这<code>2MSL</code>的等待时间内，本次连接的所有报文都已经从网络中消失，从而不会出现失效的报文出现在下次连接中而导致错误。</p>
</li>
</ul>
<p>由此可知，<code>TIME_WAIT</code> 等待 <code>2 </code>倍的 <code>MSL</code>是比较合理的。 网络中可能存在来自发送方的数据包，当这些发送方的数据包被接收方处理后又会向对方发送响应，所以一来一回需要等待 <code>2</code> 倍的时间。比如，如果被动关闭方没有收到断开连接的最后的 <code>ACK</code> 报文，就会触发超时重发 <code>FIN</code> 报文，另一方接收到 <code>FIN</code> 后，会重发 <code>ACK</code> 给被动关闭方， 一来一去正好<code>2</code>个 <code>MSL</code>。所以<code>2MSL</code>时长， 这其实是相当于<strong>至少允许报文丢失一次</strong>。比如，若 <code>ACK </code>在一个 <code>MSL</code> 内丢失，这样被动方重发的 <code>FIN </code>会在第 <code>2</code> 个 <code>MSL</code> 内到达，客户端还处于<code>TIME_WAIT</code> 状态，并未关闭，这样就可以应对这种情况，有时间重新发送一次<code>ACK</code>。</p>
<p><code>2MSL</code> 的时间是从客户端接收到 <code>FIN</code> 后发送 <code>ACK</code> 开始计时的。如果在 <code>TIME-WAIT</code> 时间内，因为客户端的 <code>ACK</code> 没有传输到服务端，客户端又接收到了服务端重发的 <code>FIN</code> 报文，那么 <code>2MSL</code> 时间将重新计时。</p>
<p>在 Linux 系统里 <code>2MSL</code> 默认是 <code>60</code> 秒，那么一个 <code>MSL</code> 也就是 <code>30</code> 秒。<code>Linux</code> 系统停留在 <code>TIME_WAIT</code> 的时间为固定的 <code>60</code> 秒。其定义在 <code>Linux</code> 内核代码里的名称为 <code>TCP_TIMEWAIT_LEN</code>：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> TCP_TIMEWAIT_LEN (60*HZ) <span class="comment">/* how long to wait to destroy TIME-WAIT state, about 60 seconds  */</span></span></span><br></pre></td></tr></table></figure>

<p>如果要修改 <code>TIME_WAIT</code> 的时间长度，只能修改 <code>Linux</code> 内核代码里 <code>TCP_TIMEWAIT_LEN</code> 的值，并重新编译 <code>Linux</code> 内核。</p>
<h1 id="五、TCP状态说明"><a href="#五、TCP状态说明" class="headerlink" title="五、TCP状态说明"></a><font size=3>五、TCP状态说明</font></h1><h2 id="1-TCP的状态"><a href="#1-TCP的状态" class="headerlink" title="1. TCP的状态"></a><font size=3>1. TCP的状态</font></h2><p>TCP 协议在建立连接、断开连接以及数据传输过程中都会呈现出现不同的状态，不同的状态采取的动作也是不同的，需要处理各个状态之间的关系。这些 TCP 状态的说明如下所示：  </p>
<ul>
<li><p><strong>CLOSED 状态</strong>： 表示一个初始状态。</p>
</li>
<li><p><strong>LISTENING 状态</strong>： 这是一个非常容易理解的状态，表示服务器端的某个 SOCKET 处于监听状态，监听客户端的连接请求，可以接受连接了。例如服务器能够提供某种服务，它会监听客户端 TCP端口的连接请求，处于 LISTENING 状态，端口是开放的，等待被客户端连接。</p>
</li>
<li><p>**SYN_SENT 状态(客户端状态)**： 当客户端调用 connect()函数连接时，它首先会发送 SYN 报文给服务器请求建立连接，因此也随即它会进入到了 SYN_SENT 状态，并等待服务器的发送三次握手中的第 2 个报文。 SYN_SENT 状态表示客户端已发送 SYN 报文。</p>
</li>
<li><p>**SYN_REVD 状态(服务端状态)**： 这个状态表示服务器接受到了 SYN 报文，在正常情况下，这个状态是服务器端的 SOCKET 在建立 TCP 连接时的三次握手过程中的一个中间状态， 很短暂，基本上用 netstat 是很难看到这种状态的，除非特意写了一个客户端测试程序，故意将三次 TCP 握手过程中最后一个 ACK 报文不予发送。因此这种状态时，当收到客户端的 ACK 报文后，它会进入到 ESTABLISHED 状态。</p>
</li>
<li><p><strong>ESTABLISHED 状态</strong>： 这个容易理解了，表示连接已经建立了。</p>
</li>
<li><p><strong>FIN_WAIT_1 和 FIN_WAIT_2 状态</strong>： 其实 FIN_WAIT_1 和 FIN_WAIT_2 状态的真正含义都是表示等待对方的 FIN 报文。而这两种状态的区别是： FIN\_WAIT_1 状态实际上是当 SOCKET 在ESTABLISHED 状态时，它想主动关闭连接，向对方发送了 FIN 报文，此时该SOCKET 即进入到FIN_WAIT_1 状态。而当对方回应 ACK 报文后，则进入到 FIN_WAIT_2 状态，当然在实际的正常情况下，无论对方何种情况下，都应该马上回应 ACK 报文，所以 FIN_WAIT_1 状态一般是比较难见到的，而 FIN_WAIT_2 状态还有时常常可以用 netstat 看到。</p>
</li>
<li><p><strong>TIME_WAIT 状态</strong>： 表示收到了对方的 FIN 报文，并发送出了 ACK 报文，就等 2MSL 后即可回到 CLOSED 可用状态了。如果 FIN_WAIT_1 状态下，收到了对方同时带 FIN 标志和 ACK 标志的报文时，可以直接进入到 TIME_WAIT 状态，而无须经过 FIN_WAIT_2 状态。</p>
</li>
<li><p><strong>CLOSE_WAIT 状态</strong>： 这种状态的含义其实是表示在等待关闭。怎么理解呢？当对方 close 一个SOCKET 后发送 FIN 报文给自己，我们的系统毫无疑问地会回应一个 ACK 报文给对方，此时则进入到CLOSE_WAIT状态。接下来呢，实际上我们真正需要考虑的事情是察看我们是否还有数据发送给对方，如果没有的话，我们也就可以 close 这个 SOCKET，发送 FIN 报文给对方，也即关闭连接。所以我们在 CLOSE_WAIT 状态下，需要完成的事情是等待我们去关闭连接。</p>
</li>
<li><p><strong>LAST_ACK 状态</strong>： 它是被动关闭一方在发送 FIN 报文后，最后等待对方的 ACK 报文。当收到 ACK报文后，也即可以进入到 CLOSED 状态了。</p>
</li>
</ul>
<h2 id="2-结合图文分析一下"><a href="#2-结合图文分析一下" class="headerlink" title="2. 结合图文分析一下"></a><font size=3>2. 结合图文分析一下</font></h2><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/tcpip007.png" alt="tcpip007" />

<ul>
<li>虚线：表示服务器的状态转移。</li>
<li>实线：表示客户端的状态转移。</li>
<li>图中所有“关闭”、“打开”都是应用程序主动处理。</li>
<li>图中所有的“超时”都是内核超时处理。</li>
</ul>
<blockquote>
<p><strong>三次握手过程：</strong></p>
</blockquote>
<ul>
<li>图中(7)：服务器的应用程序主动使服务器进入监听状态，等待客户端的连接请求。</li>
<li>图中(1)：首先客户端的应用程序会主动发起连接，发送SNY报文段给服务器，在发送之后就进入SYN_SENT状态等待服务器的SNY ACK报文段进行确认，如果在指定超时时间内服务器不进行应答确认，那么客户端将关闭连接。</li>
<li>图中(8)：处于监听状态的服务器收到客户端的连接请求（SNY报文段），那么服务器就返回一个SNY ACK报文段应答客户端的响应，并且服务器进入SYN_RCVD状态。</li>
<li>图中(1)：如果客户端收到了服务器的SNY ACK报文段，那么就进入ESTABLISHED稳定连接状态，并向服务器发送一个ACK报文段。</li>
<li>图中(9)：同时，服务器收到来自客户端的ACK报文段，表示连接成功，进入ESTABLISHED稳定连接状态，这正是我们建立连接的三次握手过程。</li>
</ul>
<blockquote>
<p><strong>四次挥手过程：</strong></p>
</blockquote>
<ul>
<li>图中(3)：一般来说，都是客户端主动发送一个FIN报文段来终止连接，此时客户端从ESTABLISHED稳定连接状态转移为FIN_WAIT_1状态，并且等待来自服务器的应答确认。</li>
<li>图中(10)：服务器收到FIN报文段，知道客户端请求终止连接，那么将返回一个ACK报文段到客户端确认终止连接，并且服务器状态由稳定状态转移为CLOSE_WAIT等待终止连接状态。</li>
<li>图中(4)：客户端收到确认报文段后，进入FIN_WAIT_2状态，等待来自服务器的主动请求终止连接，此时 <code>&#123;客户端-&gt;服务器&#125;</code> 方向上的连接已经断开。</li>
<li>图中(11)：一般来说，当客户端终止了连接之后，服务器也会终止 <code>&#123;客户端-&gt;服务器&#125;</code> 方向上的连接，因此服务器的原因程序会主动关闭该方向上的连接，发送一个FIN报文段给客户端。</li>
<li>图中(5)：处于FIN_WAIT_2的客户端收到FIN报文段后，发送一个ACK报文段给服务器。</li>
<li>图中(12)：服务器收到ACK报文段，就直接关闭，此时 <code>&#123;客户端-&gt;服务器&#125;</code> 方向上的连接已经终止，进入CLOSED状态。</li>
<li>图中(6)：客户端还会等待2MSL，以防ACK报文段没被服务器收到，这就是四次挥手的全部过程。 注意：对于图中(13)(14)(15)的这些状态都是一些比较特殊的状态，我们暂时就不详细去说了，总的来说都是一样的。</li>
</ul>
<h1 id="六、总结"><a href="#六、总结" class="headerlink" title="六、总结"></a><font size=3>六、总结</font></h1><p>主要是几张图：</p>
<ul>
<li>建立连接的三次握手</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133727320.png" alt="image-20241027133727320" style="zoom:50%;" />

<ul>
<li>断开连接的四次挥手</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133759867.png" alt="image-20241027133759867" style="zoom:50%;" />

<ul>
<li>整个通信过程</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/01HQ%E8%AF%BE%E7%A8%8B%E4%BD%93%E7%B3%BB/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/LV06-02-%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80-07-TCP%E5%8D%8F%E8%AE%AE/img/image-20241027133840905.png" alt="image-20241027133840905" style="zoom: 80%;" />

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/8a2d2090.html">LV06-02-网络基础-07-TCP协议</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2024年10月27日 - 11:49</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/8a2d2090.html" title="LV06-02-网络基础-07-TCP协议">https://sumumm.github.io/post/8a2d2090.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/LV06-%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/" rel="tag"><i class="fa fa-tag"></i> LV06-网络编程</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/46bc90c.html" rel="prev" title="LV06-02-网络基础-08-UDP协议">
                  <i class="fa fa-angle-left"></i> LV06-02-网络基础-08-UDP协议
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/482b78dc.html" rel="next" title="LV06-02-网络基础-06-以太网和数据帧">
                  LV06-02-网络基础-06-以太网和数据帧 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
